using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework.Content;

namespace Phoenix.Xna.Framework.ContentPipeline
{
    public class ContentService : ContentManager, IContentService
    {
        private const string DefaultRoot = "Content";
        private Dictionary<string, AssetTracker> _loadedAssets;
        private List<IDisposable> _disposables;

        public ContentService(Engine engine)
            : this(engine, ShortGuid.NewGuid().ToString(), DefaultRoot)
        {
        }

        public ContentService(Engine engine, string id)
            : this(engine, id, DefaultRoot)
        {
        }

        public ContentService(Engine engine, string id, string rootDirectory)
            : base(engine.Services, rootDirectory)
        {
            Engine = engine;
            ID = id;
            _loadedAssets = new Dictionary<string, AssetTracker>();
            _disposables = new List<IDisposable>();
            Engine.Services.AddService<IContentService>(this);
        }

        private string StripRootDirectory(string asset)
        {
            string strippedAsset = asset.Replace(RootDirectory, string.Empty);

            if (strippedAsset.StartsWith("\\"))
            {
                return strippedAsset.Substring(1, strippedAsset.Length - 1);
            }

            return strippedAsset;
        }

        #region ContentManager Members

        public override T Load<T>(string asset)
        {
            if (!string.IsNullOrEmpty(asset) && _loadedAssets.ContainsKey(asset))
            {
                AssetTracker trackerExisting = _loadedAssets[asset];
                T assetObject = (T)trackerExisting.Asset;
                trackerExisting.ReferenceCount++;
                return assetObject;
            }

            AssetTracker tracker = new AssetTracker();
            tracker.ReferenceCount = 1;
            tracker.AssetName = StripRootDirectory(asset);
            tracker.Asset = ReadAsset<T>(string.Format("{0}\\{1}", RootDirectory, tracker.AssetName), RecordDisposable);
            _loadedAssets.Add(asset, tracker);
            return (T)tracker.Asset;
        }

        public override void Unload()
        {
            foreach (IDisposable d in _disposables)
            {
                d.Dispose();
            }

            _disposables.Clear();
            _loadedAssets.Clear();
        }

        protected override void Dispose(bool disposing)
        {
            if (!IsDisposed)
            {
                if (disposing)
                {
                    Unload();
                }

                IsDisposed = true;
            }
        }

        #endregion

        #region IContentService Members

        public void Unload(string assetName)
        {
            if (!string.IsNullOrEmpty(assetName) && _loadedAssets.ContainsKey(assetName))
            {
                AssetTracker tracker = _loadedAssets[assetName];
                DisposeAsset(tracker.Asset);
                _loadedAssets.Remove(assetName);
            }
        }

        public void Release(string asset)
        {
            if (!string.IsNullOrEmpty(asset) && _loadedAssets.ContainsKey(asset))
            {
                AssetTracker tracker = _loadedAssets[asset];
                tracker.ReferenceCount--;

                if (tracker.ReferenceCount == 0)
                {
                    DisposeAsset(tracker.Asset);
                    _loadedAssets.Remove(asset);
                }
            }
        }

        public T Reload<T>(string asset)
        {
            if (!string.IsNullOrEmpty(asset) && _loadedAssets.ContainsKey(asset))
            {
                AssetTracker oldAssetTracker = _loadedAssets[asset];
                _loadedAssets.Remove(asset);
                DisposeAsset(oldAssetTracker.Asset);
                return Load<T>(asset);
            }
            else
            {
                return Load<T>(asset);
            }
        }

        public bool IsLoaded(string asset)
        {
            if (!string.IsNullOrEmpty(asset))
            {
                return _loadedAssets.ContainsKey(asset) ? true : false;
            }

            return false;
        }

        public int ReferenceCount(string asset)
        {
            if (!string.IsNullOrEmpty(asset))
            {
                return _loadedAssets.ContainsKey(asset) ? _loadedAssets[asset].ReferenceCount : 0;
            }

            return 0;
        }

        private void DisposeAsset(object asset)
        {
            IDisposable disposable = asset as IDisposable;

            if (disposable != null && _disposables.Contains(disposable))
            {
                disposable.Dispose();
                _disposables.Remove(disposable);
            }
        }

        private void RecordDisposable(IDisposable disposable)
        {
            _disposables.Add(disposable);
        }

        #endregion

        #region IService Members

        public Engine Engine { get; set; }

        public string ID { get; set; }

        #endregion

        #region IDisposableEx Members

        public bool IsDisposed { get; set; }

        #endregion
    }
}
